#include "parser.h"
#include "parser_helper.h"
#include "opt.h"
#include "static.h"
#include "message.h"
#include <cassert>
#include <iostream>
#include <sstream>

//----------------------------------------------------------------------------------------------------------//
Parser_C::Parser_C(string p_strDir, string p_strBenchMarkName): _strDir(p_strDir),_strBenchMarkName(p_strBenchMarkName),_pData()
{
    _pData = new ParserData_S;
    _pData->_vModule.resize(0);
    _pData->_pSdcData  = new sdcData_S;
    _pData->_pSdcData->_pClkData = new clkData_S;
    _pData->_vSpefData.resize(0);
    _pData->_vCellLibrary.resize(0);
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpModule()
{
    Dumper dumper("dumpParserModule.txt");
    for(unsigned int i=0;i<this->_pData->_vModule.size();++i){
        module_C* pModule = this->_pData->_vModule[i];
        const vector<string>& vPI  = pModule->getPI();
        const vector<string>& vPO  = pModule->getPO();
        const vector<string>& vNet = pModule-> getNet();
        const vector<cellInst_C*>& vCellInst = pModule->getCellInst(); 
        dumper.write("= Module %2d=================================================\n",i);
        dumper.write("name %s\n",pModule->getName().c_str());
        dumper.write("PI:\n");
        for(unsigned int iPI=0;iPI<vPI.size();++iPI){
            dumper.write("%s\n",vPI[iPI].c_str());
        }
        dumper.write("PO:\n");
        for(unsigned int iPO=0;iPO<vPO.size();++iPO){
            dumper.write("%s\n",vPO[iPO].c_str());
        }
        dumper.write("Net:\n");
        for(unsigned int iNet=0;iNet<vNet.size();++iNet){
            dumper.write("%s\n",vNet[iNet].c_str());
        }
        dumper.write("CellInst:\n");
        for(unsigned int iCellInst=0;iCellInst<vCellInst.size();++iCellInst){
            cellInst_C* pCellInst = vCellInst[iCellInst];
            dumper.write("%s %s\n",pCellInst->getCellInst().c_str(),pCellInst->getCellType().c_str());
            const vector<pair<string,string> >& vPairPinNet = pCellInst->getPinNetPair();
            for(unsigned int iPinNet=0;iPinNet<vPairPinNet.size();++iPinNet){
                dumper.write("\t%s %s\n",vPairPinNet[iPinNet].first.c_str(),vPairPinNet[iPinNet].second.c_str());
            }
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpSDC()
{
    Dumper dumper("dumpParserSDC.txt");
    clkData_S* pClkData = this->_pData->_pSdcData->_pClkData;
    dumper.write("clk: name:%s port:%s period:%f\n",pClkData->_strClockName.c_str(),pClkData->_strClockPort.c_str(),pClkData->_fClockPeriod);
    vector<pair<string,float> >& vInputDelayPair = this->_pData->_pSdcData->_vInputDelayPair;
    dumper.write("input_name delay\n");
    for(unsigned int i=0;i<vInputDelayPair.size();++i){
        dumper.write("%s %f\n",vInputDelayPair[i].first.c_str(),vInputDelayPair[i].second);
    }
    vector<pair<string,inputDriver_S*> >& vInputDriver = this->_pData->_pSdcData->_vInputDrive;
    dumper.write("input_name driver_cell pin rise_slew fall_slew\n");
    for(unsigned int i=0;i<vInputDriver.size();++i){
        inputDriver_S* pInputDriver = vInputDriver[i].second;
        dumper.write("%s %s %s %f %f\n"
                ,vInputDriver[i].first.c_str(),pInputDriver->_strCell.c_str(),pInputDriver->_strPin.c_str()
                ,pInputDriver->_fTransRise,pInputDriver->_fTransFall);
    }
    vector<std::pair<string,float> >& vOutputDelayPair = this->_pData->_pSdcData->_vOutputDelayPair;
    dumper.write("output_name delay\n");
    for(unsigned int i=0;i<vOutputDelayPair.size();++i){
        dumper.write("%s %f\n",vOutputDelayPair[i].first.c_str(),vOutputDelayPair[i].second);
    }
    dumper.write("output_name load:\n");
    vector<std::pair<string,float> >& vOutputLoadPair  = this->_pData->_pSdcData->_vOutputLoadPair;
    for(unsigned int i=0;i<vOutputLoadPair.size();++i){
        dumper.write("%s %f\n",vOutputLoadPair[i].first.c_str(),vOutputLoadPair[i].second);
    }
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpSPEF()
{
    Dumper dumper("dumpParserSPEF.txt");
    vector<spefData_S*>& vSpefData = this->_pData->_vSpefData;
    dumper.write("net_name lumped_cap\n");
    dumper.write("connect: type name dir\n");
    dumper.write("cap: name cap\n");
    dumper.write("res: from to res\n");
    for(unsigned int i=0;i<vSpefData.size();++i){
        spefData_S* pSpefData = vSpefData[i];
        vector<SpefConnection>&  vCon = pSpefData->_vConnection;
        vector<SpefCapacitance>& vCap = pSpefData->_vCap;
        vector<SpefResistance>&  vRes = pSpefData->_vRes;
        dumper.write("\n");
        dumper.write("%s %f\n",pSpefData->_strNetName.c_str(),pSpefData->_fLumpedCap);
        for(unsigned int iCon=0;iCon<vCon.size();++iCon){
            ostringstream ssNodeName;
            ssNodeName << vCon[iCon].nodeName;
            dumper.write("%c %s %c\n",vCon[iCon].nodeType,ssNodeName.str().c_str(),vCon[iCon].direction);
        }
        for(unsigned int iCap=0;iCap<vCap.size();++iCap){
            ostringstream ssNodeName;
            ssNodeName << vCap[iCap].nodeName;
            dumper.write("%s %lf\n",ssNodeName.str().c_str(),vCap[iCap].capacitance);
        }
        for(unsigned int iRes=0;iRes<vRes.size();++iRes){
            ostringstream ssNodeName1,ssNodeName2;
            ssNodeName1 << vRes[iRes].fromNodeName;
            ssNodeName2 << vRes[iRes].toNodeName;
            dumper.write("%s %s %lf\n",ssNodeName1.str().c_str(),ssNodeName2.str().c_str(),vRes[iRes].resistance);
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpPinInfo(Dumper& p_rDumper,const LibParserPinInfo& p_rPinInfo)
{
    if(p_rPinInfo.isInput)
        p_rDumper.write("pin: %s %.2f - %d %d\n",p_rPinInfo.name.c_str(),p_rPinInfo.capacitance,p_rPinInfo.isInput,p_rPinInfo.isClock);
    else
        p_rDumper.write("pin: %s %.2f %.2f %d %d\n",p_rPinInfo.name.c_str(),p_rPinInfo.capacitance,p_rPinInfo.maxCapacitance,p_rPinInfo.isInput,p_rPinInfo.isClock);
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpLUT(Dumper& p_rDumper,const LibParserLUT& p_rLUT,const char* p_caTimingName)
{
/*
  vector<double> loadIndices ;
  vector<double> transitionIndices ;
  vector<vector<double> > tableVals ;
*/
    p_rDumper.write(" %s\n",p_caTimingName);
    p_rDumper.write("  load ");
    for(unsigned int iLoad=0;iLoad<p_rLUT.loadIndices.size();++iLoad){
        p_rDumper.write("%.4f ",p_rLUT.loadIndices[iLoad]);
    }
    p_rDumper.write("\n");
    p_rDumper.write("  trans ");
    for(unsigned int iTrans=0;iTrans<p_rLUT.transitionIndices.size();++iTrans){
        p_rDumper.write("%.4f ",p_rLUT.transitionIndices[iTrans]);
    }
    p_rDumper.write("\n");
    p_rDumper.write("  val\n");
    for(unsigned int iVal1=0;iVal1<p_rLUT.tableVals.size();++iVal1){
        p_rDumper.write("\t");
        for(unsigned int iVal2=0;iVal2<p_rLUT.tableVals.size();++iVal2){
            p_rDumper.write("%.4f ",p_rLUT.tableVals[iVal1][iVal2]);
        }
        p_rDumper.write("\n");
    }
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpTimingInfo(Dumper& p_rDumper,const LibParserTimingInfo& p_rTimingInfo)
{
    const LibParserLUT& LUTfalldelay = p_rTimingInfo.fallDelay;
    const LibParserLUT& LUTrisedelay = p_rTimingInfo.riseDelay;
    const LibParserLUT& LUTfalltrans = p_rTimingInfo.fallTransition;
    const LibParserLUT& LUTrisetrans = p_rTimingInfo.riseTransition;
    p_rDumper.write("timing: %s %s %s\n",p_rTimingInfo.fromPin.c_str(),p_rTimingInfo.toPin.c_str(),p_rTimingInfo.timingSense.c_str());
    dumpLUT(p_rDumper,LUTfalldelay,"fall_delay");
    dumpLUT(p_rDumper,LUTrisedelay,"rise_delay");
    dumpLUT(p_rDumper,LUTfalltrans,"fall_trans");
    dumpLUT(p_rDumper,LUTrisetrans,"rise_trans");
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::dumpLibrary()
{
    Dumper dumper("dumpParserLibrary.txt");
    dumper.write("name footprint leakagePower area is_sequential dontTouch\n");
    dumper.write("pin: name cap max_cap is_input is_clock\n");
    dumper.write("timing: from_pin to_pin sense\n");
    dumper.write("lut_fall_delay load_indices trans_indices table_values\n");
    dumper.write("lut_rise_delay load_indices trans_indices table_values\n");
    dumper.write("lut_fall_trans load_indices trans_indices table_values\n");
    dumper.write("lut_rise_trans load_indices trans_indices table_values\n");
    for(unsigned int i=0;i<this->_pData->_vCellLibrary.size();++i){
        LibParserCellInfo* pCell = &(this->_pData->_vCellLibrary[i]);
        vector<LibParserPinInfo>& vPin = pCell->pins;
        vector<LibParserTimingInfo>& vTiming = pCell->timingArcs;
        dumper.write("\n");
        dumper.write("cell: %s %s %.2f %.2f %d %d\n",pCell->name.c_str(),pCell->footprint.c_str(),pCell->leakagePower,pCell->area,pCell->isSequential,pCell->dontTouch);
        for(unsigned int iPin=0;iPin<vPin.size();++iPin){
            dumpPinInfo(dumper,vPin[iPin]);
        }
        for(unsigned int iTiming=0;iTiming<vTiming.size();++iTiming){
            dumpTimingInfo(dumper,vTiming[iTiming]);
        }
    }
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::parseVerilog(string p_strFileName)
{
    VerilogParser vp (p_strFileName) ;
    string moduleName ;
    bool valid = vp.read_module (moduleName) ;
    assert (valid) ;
    //cout << "Module " << moduleName << endl << endl ;
    module_C* pModule = new module_C(moduleName);
    this->_pData->_vModule.push_back(pModule);

    do {
        string primaryInput ;
        valid = vp.read_primary_input (primaryInput) ;

        if (valid){
            //cout << "Primary input: " << primaryInput << endl ;
            pModule->addPI(primaryInput);
        }

    } while (valid) ;

    cout << endl ;

    do {
        string primaryOutput ;
        valid = vp.read_primary_output (primaryOutput) ;

        if (valid){
            //cout << "Primary output: " << primaryOutput << endl ;
            pModule->addPO(primaryOutput);
        }

    } while (valid) ;

    cout << endl ;

    do {
        string net ;
        valid = vp.read_wire (net) ;

        if (valid){
            //cout << "Net: " << net << endl ;
            pModule->addNet(net);
        }

    } while (valid) ;


    cout << endl ;
    cout << "Cell insts: " << std::endl ;

    do {
        string cellType, cellInst ;
        vector<std::pair<string, string> > pinNetPairs ;

        valid = vp.read_cell_inst (cellType, cellInst, pinNetPairs) ;

        if (valid) {
            //cout << cellType << " " << cellInst << " " ;
            //for (int i=0; i < pinNetPairs.size(); ++i) {
            //    cout << "(" << pinNetPairs[i].first << " " << pinNetPairs[i].second << ") " ;
            //}
            //cout << endl ;
            cellInst_C* pInst = new cellInst_C(cellType,cellInst,pinNetPairs);
            pModule->addInst(pInst);
        }

    } while (valid) ;

}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::parseSdc(string p_strFileName)
{
    SdcParser sp (p_strFileName) ;

    string clockName ;
    string clockPort ;
    double period ;
    bool valid = sp.read_clock (clockName, clockPort, period) ;

    assert(valid) ;
    this->_pData->_pSdcData->_pClkData->_strClockName = clockName;
    this->_pData->_pSdcData->_pClkData->_strClockPort = clockPort;
    this->_pData->_pSdcData->_pClkData->_fClockPeriod = period;
    //cout << "Clock " << clockName << " connected to port " << clockPort
    //    << " has period " << period << endl ;

    do {
        string portName ;
        double delay ;

        valid = sp.read_input_delay (portName, delay) ;

        if (valid){
            this->_pData->_pSdcData->_vInputDelayPair.push_back(make_pair(portName,delay));
            //cout << "Input port " << portName << " has delay " << delay << endl ;
            
        }

    } while (valid) ;


    do {
        string portName ;
        string driverSize ;
        string driverPin ;
        double inputTransitionFall ;
        double inputTransitionRise ;

        valid = sp.read_driver_info (portName, driverSize, driverPin,
                inputTransitionFall, inputTransitionRise) ;

        if (valid) {
            inputDriver_S* pInputDrive = new inputDriver_S;
            pInputDrive->_strCell = driverSize;
            pInputDrive->_strPin  = driverPin;
            pInputDrive->_fTransRise = inputTransitionRise;
            pInputDrive->_fTransFall = inputTransitionFall;
            this->_pData->_pSdcData->_vInputDrive.push_back(make_pair(portName,pInputDrive));
            //cout << "Input port " << portName << " is assumed to be connected to the "
            //    << driverPin << " pin of lib cell " << driverSize << endl ;
            //cout << "This virtual driver is assumed to have input transitions: "
            //    << inputTransitionFall << " (fall) and " << inputTransitionRise
            //    << " (rise)" << endl ;
        }


    } while (valid) ;

    do {
        string portName ;
        double delay ;

        valid = sp.read_output_delay (portName, delay) ;

        if (valid){
            this->_pData->_pSdcData->_vOutputDelayPair.push_back(make_pair(portName,delay));
            //cout << "Output port " << portName << " has delay " << delay << endl ;
        }

    } while (valid) ;


    do {
        string portName ;
        double load ;

        valid = sp.read_output_load (portName, load) ;

        if (valid){
            this->_pData->_pSdcData->_vOutputLoadPair.push_back(make_pair(portName,load));
            //cout << "Output port " << portName << " has load " << load << endl ;
        }

    } while (valid) ;


}
//----------------------------------------------------------------------------------------------------------//
//struct SpefNet {
//  string netName ;
//  double netLumpedCap ;
//  vector<SpefConnection> connections ;
//  vector<SpefCapacitance> capacitances ;
//  vector<SpefResistance> resistances ;
//} ;
void Parser_C::parseSpef(string p_strFileName)
{

    SpefParser sp (p_strFileName) ;

    SpefNet spefNet ;
    bool valid = sp.read_net_data (spefNet) ;

    int readCnt = 0 ;
    while (valid) {

        ++readCnt ;

        // print out the contents of the spefNet just read
        spefData_S* pSpefData = new spefData_S;
        pSpefData->_strNetName  = spefNet.netName;
        pSpefData->_fLumpedCap  = spefNet.netLumpedCap;
        pSpefData->_vConnection = spefNet.connections;
        pSpefData->_vCap        = spefNet.capacitances;
        pSpefData->_vRes        = spefNet.resistances;
        this->_pData->_vSpefData.push_back(pSpefData);
        //cout << "Net: " << spefNet.netName << endl ;
        //cout << "Net lumped cap: " << spefNet.netLumpedCap << endl ;

        //cout << "Connections: " << endl ;
        //for (unsigned int i=0; i < spefNet.connections.size(); ++i) {
        //    cout << spefNet.connections[i] << endl ;
        //}

        //cout << "Capacitances: " << endl ;
        //for (unsigned int i=0; i < spefNet.capacitances.size(); ++i) {
        //    cout << spefNet.capacitances[i] << endl ;
        //}

        //cout << "Resistances: " << endl ;
        //for (unsigned int i=0; i < spefNet.resistances.size(); ++i) {
        //    cout << spefNet.resistances[i] << endl ;
        //}

        //cout << endl ;
        valid = sp.read_net_data (spefNet) ;
    }

    cout << "Read " << readCnt << " nets in the spef file." << endl ;
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::parseLibrary(string p_strFileName)
{
    LibParser lp (p_strFileName) ;
    DebugMsg("[parseLibrary] %s \n",p_strFileName.c_str());

    double maxTransition = 0.0 ;
    bool valid = lp.read_default_max_transition(maxTransition) ;

    assert (valid) ;
    this->_pData->_fCellMaxTrans = maxTransition;
    //cout << "The default max transition defined is " << maxTransition << endl ;

    int readCnt = 0 ;
    do {
        LibParserCellInfo cell ;
        valid = lp.read_cell_info (cell) ;

        if (valid) {
            ++readCnt ;

            this->_pData->_vCellLibrary.push_back(cell);
            //cout << cell << endl ;
        }

    } while (valid) ;

    cout << "Read " << readCnt << " number of library cells" << endl ;
}
//----------------------------------------------------------------------------------------------------------//
void Parser_C::parse()
{
    string strVerilog = this->_strDir +"/" +this->_strBenchMarkName +"/" +this->_strBenchMarkName +".v";
    string strSdc     = this->_strDir +"/" +this->_strBenchMarkName +"/" +this->_strBenchMarkName +".sdc";
    string strSpef    = this->_strDir +"/" +this->_strBenchMarkName +"/" +this->_strBenchMarkName +".spef";
    string strLibrary = this->_strDir +Static_C::getOption("-lib").toStr();
    parseVerilog(strVerilog);
    parseSdc(strSdc);
    parseSpef(strSpef);
    parseLibrary(strLibrary);
}
//----------------------------------------------------------------------------------------------------------//




